﻿using System;
using System.Runtime.InteropServices;

namespace Microsoft.Win32
{
	#region Keyboard Hook Events Routine

	public class KeyPressedEventArgs
	{
		public readonly bool alt;
		public readonly bool ctrl;
		public readonly bool shift;
		public readonly string character;

		public KeyPressedEventArgs(string character, bool alt, bool ctrl, bool shift)
		{
			this.character = character;
			this.alt = alt;
			this.ctrl = ctrl;
			this.shift = shift;
		}
	}

	public delegate void KeyPressedEventHandler(object sender, KeyPressedEventArgs e);

	#endregion

	public class KeyboardHook : LocalWindowsHook, IDisposable
	{
		#region Construction

		public KeyboardHook() : base( HookType.WH_KEYBOARD )
		{
			this.HookInvoked += new HookEventHandler(this.KeyboardHookInvoked); 
		}

		#endregion

		#region Disposing

		~KeyboardHook() { Dispose( false ); }

		protected void Dispose( bool disposing )
		{
			if( IsInstalled ) Uninstall();
			if( disposing ) GC.SuppressFinalize( this );
		}

		public void Dispose() { Dispose( true ); }

		#endregion

		#region Events

		public event KeyPressedEventHandler KeyPressed;

		protected void OnKeyPressed(KeyPressedEventArgs e)
		{
			if( KeyPressed != null ) KeyPressed(this, e);
		}

		#endregion

		#region Keyboard Hook specific code

		enum VKCodes
		{
			CONTROL = 0x11,
			ALT = 0x12,
			SHIFT = 0xA0,
		}

		private void KeyboardHookInvoked(object sender, HookEventArgs e)
		{
			bool alt = GetAsyncKeyState((int)VKCodes.ALT) != 0;
			bool ctrl = GetAsyncKeyState((int)VKCodes.CONTROL) != 0;
			bool shift = GetAsyncKeyState((int)VKCodes.SHIFT) != 0;
			string str = " ";
			GetKeyNameText(e.lParam, str, str.Length + 1);
			OnKeyPressed(new KeyPressedEventArgs(str, alt, ctrl, shift));
		}

		#endregion

		#region Win32 Imports

		[DllImport("user32.dll")]
		static extern short GetAsyncKeyState(int vKey);

		[DllImport("user32.dll")]
		static extern int GetKeyNameText(IntPtr lParam, [MarshalAs(UnmanagedType.LPWStr)] string pString, int nSize);

		#endregion
	}
}
